package com.wkck.service.impl;

import cn.hutool.core.io.IoUtil;
import com.alibaba.fastjson.util.IOUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.google.common.primitives.Longs;
import com.wkck.VO.*;
import com.wkck.context.BaseContext;
import com.wkck.domain.*;
import com.wkck.dto.LResumeBackendQueryConditionsDto;
import com.wkck.dto.LResumeBackendQueryFilesConditionsDto;
import com.wkck.dto.ResumeHobbyDto;
import com.wkck.dto.ResumeHonorAwardDto;
import com.wkck.exception.BaseException;
import com.wkck.exception.FileNotFoundException;
import com.wkck.exception.UploadErrorException;
import com.wkck.mapper.*;
import com.wkck.service.LResumeService;
import com.wkck.utils.DateTimeGeneration;
import io.minio.*;
import io.minio.errors.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @Author: CK
 * @Date: 2024/03/01/20:38
 * @Description:
 */
@Service
@Slf4j
public class LResumeServiceImpl extends ServiceImpl<LResumeMapper, LResume> implements LResumeService {

    @Autowired
    private LResumeMapper resumeMapper;

    @Autowired
    private LResumeFilesMapper resumeFilesMapper;

    @Autowired
    private LResumeEducateExperienceMapper resumeEducateExperienceMapper;

    @Autowired
    private LResumePracticeExperienceMapper resumePracticeExperienceMapper;

    @Autowired
    private LResumeProjectExperienceMapper resumeProjectExperienceMapper;

    private static final String bucket = "resumes";

    @Autowired
    private MinioClient minioClient;

    /***
     * @Author: CK
     * @Date: 2024/3/1
     * @Param: [id]
     * @Description: 根据用户id获得用户所有简历信息
     */
    @Override
    public UserResumesInfoVo getUserResumesById(Integer id) {

        if (id == null) {
            throw new BaseException("用户编号为空! 查询异常!");
        }

        if (id != BaseContext.getCurrentId().intValue()) {
            throw new BaseException("用户信息不一致! 查询异常!");
        }

        List<UserResumesInfoUnIncludeOnlineResumesVo> userResumesById = resumeMapper.getUserResumesById(id);

        LambdaQueryWrapper<LResumeFiles> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(LResumeFiles::getUser_id, id);
        List<LResumeFiles> onlineResumes = resumeFilesMapper.selectList(queryWrapper);

        return UserResumesInfoVo.builder()
                .resumesInfo(userResumesById)
                .onlineResumeFiles(onlineResumes)
                .build();
    }

    /***
     * @Author: CK
     * @Date: 2024/3/1
     * @Description: 添加简历基本信息
     */
    @Override
    public Map<String, Object> insertUserBaseInfo(LResume resume) {
        long id = IdWorker.getId(); // mybatis-plus 自带的雪花算法生成
        resume.setId(id);

        resume.setCreate_time(DateTimeGeneration.now());
        int row = resumeMapper.insert(resume);

        HashMap<String, Object> response = new HashMap<>();
        response.put("id", String.valueOf(id));
        response.put("flag", row > 0);

        return response;
    }

    /***
     * @Author: CK
     * @Date: 2024/3/1
     * @Param: [file, id]
     * @Description: 添加简历头像
     */
    @Override
    public Boolean insertUserBaseAvatarById(String localFilePath, String filename, Long id, Integer userId) {
        File file = new File(localFilePath);
        if (!file.exists()) {
            throw new FileNotFoundException("文件未找到，上传失败!");
        }

        if (id == null) {
            throw new BaseException("上传异常: id为空");
        }

        if (userId == null) {
            throw new BaseException("上传异常: id为空");
        }

        String location = userId + "/" + id + "/" + filename;

        try {
            minioClient.uploadObject(
                    UploadObjectArgs.builder()
                            .bucket(bucket)
                            .object(location)
                            .filename(localFilePath)
                            .build());
            log.info("图片上传成功, {}", location);
        } catch (Exception e) {
            throw new UploadErrorException("图片上传失败");
        }

        LambdaUpdateWrapper<LResume> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper
                .eq(LResume::getId, id)
                .eq(LResume::getUser_id, userId)
                .set(LResume::getResume_avatar, bucket + "/" + location);

        this.update(updateWrapper);
        log.info("数据库写入结束");

        return true;
    }

    /***
     * @Author: CK
     * @Date: 2024/3/3
     * @Param: [path, filename, size, id]
     * @Description: 添加简历文件
     */
    @Override
    public Boolean insertUserResumeFile(String path, String filename, long size, Integer id) {
        if (id == null) {
            throw new BaseException("个人信息解析异常, 不允许上传!");
        }

        File file = new File(path);
        if (!file.exists()) {
            throw new FileNotFoundException("文件未找到，上传失败!");
        }

        LambdaQueryWrapper<LResumeFiles> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(LResumeFiles::getUser_id, id);
        Integer count = resumeFilesMapper.selectCount(queryWrapper);
        if (count >= 5) {
            throw new BaseException("用户简历数量已达上限！不允许上传");
        }

        long online_file_id = IdWorker.getId();

        String location = id + "/" + online_file_id + "/" + filename;

        try {
            minioClient.uploadObject(
                    UploadObjectArgs.builder()
                            .bucket(bucket)
                            .object(location)
                            .filename(path)
                            .build()
            );
        } catch (Exception e) {
            throw new UploadErrorException("简历文件上传失败");
        }

        log.info("简历上传成功 准备写入DataBase ====> {}", LocalDateTime.now());

        LResumeFiles info = LResumeFiles.builder()
                .id(online_file_id)
                .file_name(filename)
                .file_size(size)
                .key_md5(getFileMd5(file))
                .location(location)
                .bucket(bucket)
                .create_time(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
                .update_time(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
                .create_user(BaseContext.getCurrentId().intValue())
                .update_user(BaseContext.getCurrentId().intValue())
                .user_id(id)
                .build();

        int row = resumeFilesMapper.insert(info);

        if (row == 0) {
            log.error("DataBase writing exception =====> {}", LocalDateTime.now());
            return false;
        }

        return true;
    }

    /**
     * @Author: CK
     * @Date: 2024/3/4
     * @Param: [id]
     * @Description: 根据在线简历id下载简历
     */
    @Override
    public void downloadUserFileById(String id, HttpServletResponse response) {

        if (!Strings.isNotEmpty(id)) {
            throw new BaseException("用户信息为空 不允许进行操作！");
        }

        long fileId = Long.parseLong(id);

        LambdaQueryWrapper<LResumeFiles> resumeFile = new LambdaQueryWrapper<>();
        resumeFile.eq(LResumeFiles::getId, fileId);
        LResumeFiles fileInfo = resumeFilesMapper.selectOne(resumeFile);

        log.info("开始下载 ====> {}", LocalDateTime.now());

        try {
            InputStream inputStream = minioClient.getObject(
                    GetObjectArgs.builder()
                            .bucket(bucket)
                            .object(fileInfo.getLocation())
                            .build()
            );
            OutputStream outputStream = response.getOutputStream();
            response.setContentType("application/octet-stream");
            response.setCharacterEncoding("UTF-8");
            response.addHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileInfo.getFile_name(), "UTF-8"));
//            byte[] bytes = new byte[1024];
//            int len = 0;
//            while ((len = inputStream.read(bytes)) > 0) {
//                outputStream.write(bytes, 0, len);
//            }
            IoUtil.copy(inputStream, outputStream);
            outputStream.flush();
            inputStream.close();
            outputStream.close();

            log.info("下载成功 =====> {}", LocalDateTime.now());
        } catch (Exception e) {
            throw new BaseException("下载异常！！");
        }
    }

    /***
     * @Author: CK
     * @Date: 2024/3/5
     * @Param: [id]
     * @Description: 根据id删除用户的在线简历文件
     */
    @Override
    public Boolean deleteUserOnlineResumeFileById(String id) {

        if (!Strings.isNotEmpty(id)) {
            throw new BaseException("用户信息异常 不允许操作！");
        }

        LResumeFiles file = resumeFilesMapper.selectById(Long.parseLong(id));

        int deleteRow = resumeFilesMapper.deleteById(Long.parseLong(id));
        log.info("删除数据成功 ====> {}", deleteRow);

        try {
            minioClient.removeObject(
                    RemoveObjectArgs.builder()
                            .bucket(file.getBucket())
                            .object(file.getLocation())
                            .build()
            );
        } catch (Exception e) {
            throw new BaseException("删除文件异常！");
        }
        log.info("删除文件成功 =====> {}", LocalDateTime.now());

        return true;
    }

    /***
     * @Author: CK
     * @Date: 2024/3/5
     * @Param: [edu]
     * @Description: 修改或添加用户简历的教育经历信息
     */
    @Override
    public Boolean modifyOrSaveUserResumeEduExperienceInfo(LResumeEducateExperience edu) {
        boolean type = edu.getId() == null;

        if (type) {
            // 新增
            int row = resumeEducateExperienceMapper.insert(edu);
            return row > 0;
        } else {
            // 修改
            LambdaUpdateWrapper<LResumeEducateExperience> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper
                    .eq(LResumeEducateExperience::getResume_id, edu.getResume_id())
                    .eq(LResumeEducateExperience::getId, edu.getId());

            int row = resumeEducateExperienceMapper.update(edu, updateWrapper);
            return row > 0;
        }
    }

    /***
     * @Author: CK
     * @Date: 2024/3/5
     * @Param: [edu]
     * @Description: 修改或添加用户简历的实习经历信息
     */
    @Override
    public Boolean modifyOrSaveUserResumePraExperienceInfo(LResumePracticeExperience pra) {
        boolean type = pra.getId() == null;

        if (type) {
            // 新增
            int row = resumePracticeExperienceMapper.insert(pra);
            return row > 0;
        } else {
            // 修改
            LambdaUpdateWrapper<LResumePracticeExperience> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper
                    .eq(LResumePracticeExperience::getResume_id, pra.getResume_id())
                    .eq(LResumePracticeExperience::getId, pra.getId());
            return resumePracticeExperienceMapper.update(pra, updateWrapper) > 0;
        }
    }

    /***
     * @Author: CK
     * @Date: 2024/3/5
     * @Param: [edu]
     * @Description: 修改或添加用户简历的项目经历信息
     */
    @Override
    public Boolean modifyOrSaveUserResumeProExperienceInfo(LResumeProjectExperience pro) {
        boolean type = pro.getId() == null;

        if (type) {
            // 新增
            int row = resumeProjectExperienceMapper.insert(pro);
            return row > 0;
        } else {
            // 修改
            LambdaUpdateWrapper<LResumeProjectExperience> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper
                    .eq(LResumeProjectExperience::getResume_id, pro.getResume_id())
                    .eq(LResumeProjectExperience::getId, pro.getId());
            return resumeProjectExperienceMapper.update(pro, updateWrapper) > 0;
        }
    }

    /***
     * @Author: CK
     * @Date: 2024/3/7
     * @Param: [resumeId, userId]
     * @Description: 根据用户id和简历id得到简历信息
     */
    @Override
    public UserResumesInfoUnIncludeOnlineResumesVo getUserResumeInfoByUserIdAndResumeId(Integer userId, Long resumeId) {

        if (userId == null) {
            throw new BaseException("用户编号为空! 查询异常!");
        }

        if (resumeId == null) {
            throw new BaseException("简历编号为空！ 查询异常！");
        }

        if (userId != BaseContext.getCurrentId().intValue()) {
            throw new BaseException("用户信息不一致! 查询异常!");
        }

        LambdaQueryWrapper<LResume> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(LResume::getUser_id, userId)
                .eq(LResume::getId, resumeId);
        LResume condition = resumeMapper.selectOne(queryWrapper);
        if (Objects.isNull(condition)) {
            throw new BaseException("无对应简历信息！操作执行异常");
        }

        return resumeMapper.getUserResumesByUserIdAndResumeId(userId, resumeId);
    }

    /***
    * @Author: CK
    * @Date: 2024/3/28
    * @Param: [resumeId]
    * @Description: 根据简历id得到简历信息
    */
    @Override
    public UserResumesInfoUnIncludeOnlineResumesVo getUserResumeInfoByResumeId(Long resumeId) {

        if (resumeId == null) {
            throw new BaseException("简历编号为空！ 查询异常！");
        }

        LResume condition = resumeMapper.selectById(resumeId);
        if (Objects.isNull(condition)) {
            throw new BaseException("无对应简历信息！操作执行异常");
        }

        return resumeMapper.getUserResumesByResumeId(resumeId);
    }

    /***
     * @Author: CK
     * @Date: 2024/3/10
     * @Param: [id]
     * @Description: 根据简历id删除简历信息
     */
    @Override
    public Boolean deleteUserResumeById(Long id) {
        if (Objects.isNull(id)) {
            throw new BaseException("编号为空 不允许操作！");
        }

        LambdaQueryWrapper<LResumeEducateExperience> eduWrapper = new LambdaQueryWrapper<>();
        eduWrapper.eq(LResumeEducateExperience::getResume_id, id);
        resumeEducateExperienceMapper.delete(eduWrapper);
        log.info("删除教育经历成功 ===> {}", id);

        LambdaQueryWrapper<LResumePracticeExperience> praWrapper = new LambdaQueryWrapper<>();
        praWrapper.eq(LResumePracticeExperience::getResume_id, id);
        resumePracticeExperienceMapper.delete(praWrapper);
        log.info("删除实习经历成功 ===> {}", id);

        LambdaQueryWrapper<LResumeProjectExperience> proWrapper = new LambdaQueryWrapper<>();
        proWrapper.eq(LResumeProjectExperience::getResume_id, id);
        resumeProjectExperienceMapper.delete(proWrapper);
        log.info("删除项目经历成功 ===> {}", id);

        LResume resume = resumeMapper.selectById(id);
        String location = resume.getResume_avatar().replaceFirst(bucket + "/", "");
        try {
            minioClient.removeObject(
                    RemoveObjectArgs.builder()
                            .bucket(bucket)
                            .object(location)
                            .build()
            );
            log.info("简历图片删除成功 ===> {}", id);
        } catch (Exception e) {
            throw new BaseException("简历图片删除失败");
        }

        resumeMapper.deleteById(id);
        log.info("删除简历信息成功 ===> {}", id);

        return true;
    }

    /***
     * @Author: CK
     * @Date: 2024/3/10
     * @Param: [localFilePath, filename, id, userId]
     * @Description: 编辑用户头像
     */
    @Override
    public Boolean modifyUserResumeBaseAvatarById(String localFilePath, String filename, Long id, Integer userId) {
        File file = new File(localFilePath);
        if (!file.exists()) {
            throw new FileNotFoundException("文件未找到，上传失败!");
        }

        if (id == null) {
            throw new BaseException("上传异常: id为空");
        }

        if (userId == null) {
            throw new BaseException("上传异常: id为空");
        }

        LambdaQueryWrapper<LResume> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper
                .eq(LResume::getUser_id, userId)
                .eq(LResume::getId, id);
        LResume resumeInfo = resumeMapper.selectOne(queryWrapper);

        String trueAvatarLocation = resumeInfo.getResume_avatar().replaceFirst(bucket + "/", "");

        try {
            minioClient.removeObject(
                    RemoveObjectArgs.builder()
                            .bucket(bucket)
                            .object(trueAvatarLocation)
                            .build()
            );
        } catch (Exception e) {
            throw new BaseException("头像处理异常！");
        }

        String location = userId + "/" + id + "/" + filename;

        try {
            minioClient.uploadObject(
                    UploadObjectArgs.builder()
                            .bucket(bucket)
                            .object(location)
                            .filename(localFilePath)
                            .build());
            log.info("图片上传成功, {}", location);
        } catch (Exception e) {
            throw new UploadErrorException("图片上传失败");
        }

        LambdaUpdateWrapper<LResume> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper
                .eq(LResume::getId, id)
                .eq(LResume::getUser_id, userId)
                .set(LResume::getResume_avatar, bucket + "/" + location);

        this.update(updateWrapper);
        log.info("数据库写入结束");

        return true;
    }

    /***
     * @Author: CK
     * @Date: 2024/3/11
     * @Param: [honorAward]
     * @Description: 修改用户简历荣誉奖项
     */
    @Override
    public Boolean modifyUserResumeHonorAward(ResumeHonorAwardDto honorAward) {
        if (honorAward.getUser_id() == null) {
            throw new BaseException("用户编号为空! 操作异常!");
        }

        if (honorAward.getResume_id() == null) {
            throw new BaseException("简历编号为空！ 操作异常！");
        }

        if (honorAward.getUser_id() != BaseContext.getCurrentId().intValue()) {
            throw new BaseException("用户信息不一致! 操作异常!");
        }

        LambdaQueryWrapper<LResume> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(LResume::getUser_id, honorAward.getUser_id())
                .eq(LResume::getId, honorAward.getResume_id());
        LResume condition = resumeMapper.selectOne(queryWrapper);
        if (Objects.isNull(condition)) {
            throw new BaseException("无对应简历信息！操作执行异常");
        }

        LambdaUpdateWrapper<LResume> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper
                .set(LResume::getHonor_award, honorAward.getContent())
                .eq(LResume::getUser_id, honorAward.getUser_id())
                .eq(LResume::getId, honorAward.getResume_id());
        LResume resume = new LResume();
        BeanUtils.copyProperties(honorAward, resume);

        resumeMapper.update(resume, updateWrapper);
        log.info("操作用户简历荣誉奖项成功 ===> {}, {}, {}", honorAward.getUser_id(), honorAward.getResume_id(), DateTimeGeneration.now());

        return true;
    }

    /***
     * @Author: CK
     * @Date: 2024/3/11
     * @Param: [hobbyDto]
     * @Description: 修改用户简历个人兴趣与自我评价
     */
    @Override
    public Boolean modifyUserResumeHobby(ResumeHobbyDto hobbyDto) {
        if (hobbyDto.getUser_id() == null) {
            throw new BaseException("用户编号为空! 操作异常!");
        }

        if (hobbyDto.getResume_id() == null) {
            throw new BaseException("简历编号为空！ 操作异常！");
        }

        if (hobbyDto.getUser_id() != BaseContext.getCurrentId().intValue()) {
            throw new BaseException("用户信息不一致! 操作异常!");
        }

        LambdaQueryWrapper<LResume> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(LResume::getUser_id, hobbyDto.getUser_id())
                .eq(LResume::getId, hobbyDto.getResume_id());
        LResume condition = resumeMapper.selectOne(queryWrapper);
        if (Objects.isNull(condition)) {
            throw new BaseException("无对应简历信息！操作执行异常");
        }

        LambdaUpdateWrapper<LResume> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper
                .set(LResume::getHobby, hobbyDto.getHobby())
                .eq(LResume::getUser_id, hobbyDto.getUser_id())
                .eq(LResume::getId, hobbyDto.getResume_id());
        LResume resume = new LResume();
        BeanUtils.copyProperties(hobbyDto, resume);

        resumeMapper.update(resume, updateWrapper);
        log.info("操作用户简历个人兴趣与自我评价成功 ===> {}, {}, {}", hobbyDto.getUser_id(), hobbyDto.getResume_id(), DateTimeGeneration.now());

        return true;
    }

    /***
     * @Author: CK
     * @Date: 2024/3/27
     * @Param: [dto]
     * @Description: 得到系统中所有简历
     */
    @Override
    public PageResult getAllUsersPlatformResume(LResumeBackendQueryConditionsDto dto) {
        if (Objects.isNull(dto)) {
            throw new BaseException("参数异常 不允许查询");
        }

        if (dto.getCurrentPage() == null || dto.getSize() == null) {
            throw new BaseException("参数异常 不允许查询");
        }

        PageHelper.startPage(dto.getCurrentPage(),dto.getSize());

        Page<BackendUserPlatFormVo> page = resumeMapper.getAllUsersPlatformResume(dto);

        return PageResult.builder()
                .total(page.getTotal())
                .records(page.getResult())
                .build();
    }

    @Override
    public PageResult getAllUsersFileResume(LResumeBackendQueryFilesConditionsDto dto) {
        if (Objects.isNull(dto)) {
            throw new BaseException("参数异常 不允许查询");
        }

        if (dto.getCurrentPage() == null || dto.getSize() == null) {
            throw new BaseException("参数异常 不允许查询");
        }

        PageHelper.startPage(dto.getCurrentPage(),dto.getSize());

        Page<BackendUserFilesVo> page = resumeMapper.getAllUsersFilesResume(dto);

        return PageResult.builder()
                .total(page.getTotal())
                .records(page.getResult())
                .build();
    }

    /***
     * @Author: CK
     * @Date: 2024/3/28
     * @Param: [map]
     * @Description: 删除简历文件ids
     */
    @Override
    public Boolean removeFilesByIds(List<String> ids) {
//        resumeFilesMapper.deleteBatchIds(ids);
        if (Objects.isNull(ids) || ids.size() == 0) {
            throw new BaseException("无可删除数据！");
        }

        List<LResumeFiles> files = resumeFilesMapper.selectBatchIds(ids);

        resumeFilesMapper.deleteBatchIds(ids);
        log.info("删除Database中数据成功!");

        for (LResumeFiles file : files) {
            try {
                minioClient.removeObject(
                        RemoveObjectArgs.builder()
                                .bucket(file.getBucket())
                                .object(file.getLocation())
                                .build()
                );
            } catch (Exception e) {
                throw new BaseException("删除文件异常！");
            }
            log.info("删除文件成功 =====> {}", LocalDateTime.now());
        }

        log.info("删除文件结束 ====> {}", DateTimeGeneration.now());

        return true;
    }

    private String getFileMd5(File file) {
        try (FileInputStream fileInputStream = new FileInputStream(file)) {
            return DigestUtils.md5DigestAsHex(fileInputStream);
        } catch (Exception e) {
            log.error("获取文件md5失败");
            return null;
        }
    }
}
